﻿using System;
using System.Runtime.InteropServices;
using System.Threading;
using RoMBot.Classes;
using RoMBot.MemoryLib;

namespace RoMBot
{
    [StructLayout( LayoutKind.Sequential )]
    public struct NativeMessage
    {
        public IntPtr handle;
        public uint msg;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public System.Drawing.Point p;
    }

    public class Keyboard
    {
        // Fields
        //private const int delay = 50;
        //private const uint MAPVK_VK_TO_CHAR = 2;
        //private const uint MAPVK_VK_TO_VSC = 0;
        //private const uint MAPVK_VK_TO_VSC_EX = 4;
        //private const uint MAPVK_VSC_TO_VK = 1;
        //private const uint MAPVK_VSC_TO_VK_EX = 3;
        //private const uint POSTMESSAGE_EXTENDED = 0x1000001;
        //private const uint POSTMESSAGE_NORMAL = 1;
        //private const uint POSTMESSAGE_UP_EXTENDED = 0xc1000001;
        //private const uint POSTMESSAGE_UP_NORMAL = 0xc0000001;
        //private const uint WM_KEYDOWN = 0x100;
        //private const uint WM_KEYUP = 0x101;

        //private const uint WM_KEYFIRST = 0x0100;
        //private const uint WM_KEYLAST = 0x0108;

        //internal static void AttachHotKey( System.Windows.Forms.Keys key, EventHandler handler )
        //{
        //    var hotkey = new ManagedWinapi.Hotkey
        //             {
        //                 WindowsKey = false,
        //                 KeyCode = key
        //             };
        //    hotkey.HotkeyPressed += handler;
        //    try
        //    {
        //        hotkey.Enabled = true;
        //        Bot.Debug( "Registered hotkey " + key );
        //    }
        //    catch ( ManagedWinapi.HotkeyAlreadyInUseException )
        //    {
        //        Bot.Error( "Could not register hotkey " + key + " (already in use)." );
        //    }
        //}

        public static bool IsKeyPressedInApp( Key key, Key modifier = Key.VK_NONE )
        {
            var wnd = User32.GetForegroundWindow();
            if ( wnd == Memory.WindowHandle || wnd == Bot.MainWindowHandle )
            {
                return TestKeyPressed( key, modifier );
            }
            return false;
        }

        private static void Hold( Key key, Key modifier = Key.VK_NONE )
        {
            var scan = ( uint ) key;
            if ( User32.MapVirtualKey( ( uint ) key, 0 ) != 0 )
            {
                scan = User32.MapVirtualKey( ( uint ) key, 0 );
            }
            //Hold( key, modifier );
            //return;
            uint lparam;
            if ( IsExtendedKey( key ) )
            {
                lparam = ( scan << 0x10 ) | 0x1000001;
            }
            else
            {
                lparam = ( scan << 0x10 ) | 1;
            }
            if ( modifier != Key.VK_NONE )
            {
                var ks = new byte[ 0x100 ];
                User32.GetKeyboardState( ks );
                if ( ( ks[ ( int ) modifier ] & 0x80 ) != 1 )
                {
                    ks[ ( int ) modifier ] = ( byte ) ( ks[ ( int ) modifier ] | 0x80 );
                }
                User32.SetKeyboardState( ks );
            }
            User32.PostMessage( Memory.WindowHandle.ToInt32(), 0x100, new UIntPtr( ( uint ) key ), new UIntPtr( lparam ) );
        }

        private static void Release( Key key, Key modifier = Key.VK_NONE )
        {
            var scan = ( uint ) key;
            if ( User32.MapVirtualKey( ( uint ) key, 0 ) != 0 )
            {
                scan = User32.MapVirtualKey( ( uint ) key, 0 );
            }
            //Release( key, modifier );
            //return;
            uint lparam;
            if ( IsExtendedKey( key ) )
            {
                lparam = ( scan << 0x10 ) | 0xc1000001;
            }
            else
            {
                lparam = ( scan << 0x10 ) | 0xc0000001;
            }
            User32.PostMessage( Memory.WindowHandle.ToInt32(), 0x101, new UIntPtr( ( uint ) key ), new UIntPtr( lparam ) );
        }

        public static bool IsExtendedKey( Key key )
        {
            return ( ( ( ( ( ( key == Key.VK_INSERT ) || ( key == Key.VK_DELETE ) ) || ( ( key == Key.VK_HOME ) || ( key == Key.VK_END ) ) ) || ( ( ( key == Key.VK_DIVIDE ) || ( key == Key.VK_LWIN ) ) || ( ( key == Key.VK_RWIN ) || ( key == Key.VK_PRIOR ) ) ) ) || ( ( ( key == Key.VK_NEXT ) || ( key == Key.VK_LEFT ) ) || ( ( key == Key.VK_RIGHT ) || ( key == Key.VK_UP ) ) ) ) || ( key == Key.VK_DOWN ) );
        }

        public static void KeyPress( Key key, Key modifier = Key.VK_NONE, int sleepTime = 25 )
        {
            HoldKey( key, modifier );
            Thread.Sleep( sleepTime );
            ReleaseKey( key, modifier );
        }

        public static bool TestKeyPressed( Key key, Key modifier = Key.VK_NONE )
        {
            return User32.GetAsyncKeyState( ( int ) key ) != 0;
        }

        public static void HoldKey( Key key, Key modifier = Key.VK_NONE )
        {
            byte keybit = 0, oppbit = 0; // The bit for the key and the bit for the opposite key
            if ( key == Profile.HotKeys.MOVE_FORWARD.Key )
            {
                keybit = 1;
                oppbit = 2;
            }
            else if ( key == Profile.HotKeys.MOVE_BACKWARD.Key )
            {
                keybit = 2;
                oppbit = 1;
            }
            else if ( key == Profile.HotKeys.STRAFF_RIGHT.Key )
            {
                keybit = 4;
                oppbit = 8;
            }
            else if ( key == Profile.HotKeys.STRAFF_LEFT.Key )
            {
                keybit = 8;
                oppbit = 4;
            }
            else if ( key == Profile.HotKeys.ROTATE_RIGHT.Key )
            {
                keybit = 16;
                oppbit = 32;
            }
            else if ( key == Profile.HotKeys.ROTATE_LEFT.Key )
            {
                keybit = 32;
                oppbit = 16;
            }
            else
            {
                Hold( key, modifier ); // Not a move key. Fall back to original function.
                return;
            }

            // Get current move keys pressed
            var keyspressed = Memory.ReadBytePtr( Addresses.staticbase_char, Addresses.moveKeysPressed_offset );

            // Set 'key' pressed
            if ( !Helpers.BitAnd( keyspressed, keybit ) )
            {
                keyspressed += keybit;

                // Unset opposite key
                if ( Helpers.BitAnd( keyspressed, oppbit ) )
                {
                    keyspressed -= oppbit;
                }

                // Write result to memory
                Memory.WriteBytePtr( Addresses.staticbase_char, Addresses.moveKeysPressed_offset, keyspressed );
            }
        }

        public static void ReleaseKey( Key key, Key modifier = Key.VK_NONE )
        {
            byte keybit = 0; // The bit for the key and the bit for the opposite key
            if ( key == Profile.HotKeys.MOVE_FORWARD.Key )
            {
                keybit = 1;
            }
            else if ( key == Profile.HotKeys.MOVE_BACKWARD.Key )
            {
                keybit = 2;
            }
            else if ( key == Profile.HotKeys.STRAFF_RIGHT.Key )
            {
                keybit = 4;
            }
            else if ( key == Profile.HotKeys.STRAFF_LEFT.Key )
            {
                keybit = 8;
            }
            else if ( key == Profile.HotKeys.ROTATE_RIGHT.Key )
            {
                keybit = 16;
            }
            else if ( key == Profile.HotKeys.ROTATE_LEFT.Key )
            {
                keybit = 32;
            }
            else
            {
                Release( key, modifier ); // Not a move key. Fall back to original function.
                return;
            }

            // Get current move keys pressed
            var keyspressed = Memory.ReadBytePtr( Addresses.staticbase_char, Addresses.moveKeysPressed_offset );

            // Set 'key' pressed
            if ( Helpers.BitAnd( keyspressed, keybit ) )
            {
                keyspressed -= keybit;
                // Write result to memory
                Memory.WriteBytePtr( Addresses.staticbase_char, Addresses.moveKeysPressed_offset, keyspressed );
            }
        }
    }
}
